﻿#include "framework.h"
#include <math.h>
#include <time.h>
#include "MainWindow.h"
#include "Resource.h"

// 函数声明
static void Game_Delete(Game*);
static void Game_CreatePictures(Game*);
static void Game_DeletePictures(Game*);
static void Game_GameOver(Game*);
static BOOL Game_IsWon(Game*);
static void Game_MovePicture(Game*, UINT);
static void Game_SetDifficulty(Game*, GameDifficulty);
static void Game_SetPicture(Game*, HBITMAP);
static void Game_SetPictureBorder(Game*, BOOL);
static void Game_SwapPictures(Game*, int, int);
static void Game_Start(Game*);

/**
 * 创建游戏。
 * @param callback  回调
 * @param drawer    绘图者
 * @param gameBoard 游戏区域
 * @return 游戏
 */
Game* New_Game(HWND callback, HDC drawer, HWND gameBoard) {
    HBITMAP bitmap = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_PIC));
    Game* game = (Game*)calloc(1, sizeof(Game));

    // 初始化数据
    game->mCallback = callback;
    game->mGameBoard = gameBoard;
    game->mDrawer = drawer;
    game->mImageDC = CreateCompatibleDC(drawer);
    game->mImage = CreateCompatibleBitmap(drawer, GAME_BOARD_WIDTH, GAME_BOARD_HEIGHT);
    SelectObject(game->mImageDC, game->mImage);
    game->mStatus = END;
    game->mDifficulty = EASY;
    game->mPicHasBorder = TRUE;

    // 初始化函数
    game->deleteSelf = Game_Delete;
    game->movePicture = Game_MovePicture;
    game->setDifficulty = Game_SetDifficulty;
    game->setPicture = Game_SetPicture;
    game->setPictureBorder = Game_SetPictureBorder;
    game->start = Game_Start;

    // 设置图片
    Game_SetPicture(game, bitmap);

    // 置随机数种子
    srand((UINT)time(NULL));

    return game;
}

/**
 * 删除游戏。
 * @param game  游戏
 */
static void Game_Delete(Game* game) {
    Game_DeletePictures(game);
    DeleteObject(game->mImage);
    DeleteDC(game->mImageDC);
}

/**
 * 创建所有图片控件。
 * @param game  游戏
 */
static void Game_CreatePictures(Game* game) {
    int left, top;                                          // 图片控件位置
    int baseCount = (int)sqrt((double)game->mDifficulty);   // 一行（列）的图片控件数
    int size = EMPTY_AREA_WIDTH / baseCount;                // 图片控件尺寸
    RECT rect = { 0 };                                      // 图片控件位置、尺寸
    int j = 0;                                              // 换行标志，0为第一行，1为第二行，依次类推
    int i = 0;                                              // 循环变量

    // 删除所有图片
    Game_DeletePictures(game);

    // 创建所有图片控件
    for (i = 0; i < game->mDifficulty; i++) {
        if (i % baseCount == 0 && i != 0) { // 如果一行满了，换到下一列
            j++;
        }

        // 计算左上角坐标
        left = EMPTY_AREA_X + size * (i % baseCount);
        top = EMPTY_AREA_Y + size * j;

        // 设置位置、尺寸
        SetRect(&rect, left, top, left + size, top + size);

        // 创建图片控件
        game->mPictures[i] = New_PictureCtrl(game->mPicHasBorder, IDC_PICTURE_CTRL + i, game->mImageDC, game->mGameBoard, rect);
    }
}

/**
 * 删除所有图片控件。
 * @param game  游戏
 */
static void Game_DeletePictures(Game* game) {
    for (int i = 0; i < MAX_PICTURE_CTRL_COUNT; i++) {
        if (game->mPictures[i] == NULL) {
            break;
        }
        game->mPictures[i]->deleteSelf(game->mPictures[i]);
        game->mPictures[i] = NULL;
    }
}

/**
 * 游戏结束。
 * @param game  游戏
 */
static void Game_GameOver(Game* game) {
    // 将状态设置为已结束
    game->mStatus = END;

    // 发送通知
    SendMessage(game->mCallback, UM_GAME_OVER, 0, 0);
}

/**
 * 判断是否胜利。
 * @param game  游戏
 * @return 为 TRUE 胜利，为 FALSE 没胜利
 */
static BOOL Game_IsWon(Game* game) {
    BOOL result = FALSE;
    for (int i = 0; i < game->mDifficulty; i++) {
        result = game->mPictures[i]->isOnOriginalPosition(game->mPictures[i]);
        if (!result) {
            break;
        }
    }
    return result;
}

/**
 * 移动图片控件。
 * @param game  游戏
 * @param id    图片控件的 id
 */
static void Game_MovePicture(Game* game, UINT id) {
    POINT position = { 0 };
    PictureCtrl* pictureCtrl = game->mPictures[id - IDC_PICTURE_CTRL];

    if (game->mStatus != PLAYING) {
        return;
    }

    pictureCtrl->getPosition(game->mPictures[id - IDC_PICTURE_CTRL], &position);

    // 如果要移动的图片控件与空点相邻，则移动
    if (position.y == game->mEmptyPoint.y && abs(position.x - game->mEmptyPoint.x) == pictureCtrl->mSize ||
        position.x == game->mEmptyPoint.x && abs(position.y - game->mEmptyPoint.y) == pictureCtrl->mSize) {
        SetWindowPos(pictureCtrl->mHandle, HWND_TOP, game->mEmptyPoint.x, game->mEmptyPoint.y, 0, 0, SWP_NOSIZE);
        game->mEmptyPoint = position;

        // 判断是否胜利
        if (Game_IsWon(game)) {
            Game_GameOver(game);
        }
    }
}

/**
 * 设置难度。
 * @param game  游戏
 * @param diff  难度
 */
static void Game_SetDifficulty(Game* game, GameDifficulty diff) {
    // 设置难度
    game->mDifficulty = diff;

    // 重新创建所有图片
    Game_CreatePictures(game);

    // 将状态设置为已结束
    game->mStatus = END;
}

/**
 * 设置图片控件的图片。
 * @param game      游戏
 * @param bitmap    图片
 */
static void Game_SetPicture(Game* game, HBITMAP image) {
    BITMAP bmpInfo = { 0 };
    HDC bitmapDC = CreateCompatibleDC(game->mDrawer);
    RECT emptyAreaRect = { EMPTY_AREA_X, EMPTY_AREA_Y, EMPTY_AREA_X + EMPTY_AREA_WIDTH, EMPTY_AREA_Y + EMPTY_AREA_HEIGHT };

    // 将图片画到游戏区域上
    GetObject(image, sizeof(bmpInfo), &bmpInfo);
    SelectObject(bitmapDC, image);
    StretchBlt(game->mImageDC, 0, 0, GAME_BOARD_WIDTH, GAME_BOARD_HEIGHT, bitmapDC, 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, SRCCOPY);
    BitBlt(game->mDrawer, 0, 0, GAME_BOARD_WIDTH, GAME_BOARD_HEIGHT, game->mImageDC, 0, 0, SRCCOPY);
    DeleteDC(bitmapDC);

    // 画中间的空白区域
    SetBkColor(game->mDrawer, EMPTY_AREA_COLOR);
    ExtTextOut(game->mDrawer, 0, 0, ETO_OPAQUE, &emptyAreaRect, NULL, 0, NULL);

    // 通知游戏区域重绘
    InvalidateRect(game->mGameBoard, NULL, FALSE);

    // 创建所有图片
    Game_CreatePictures(game);

    // 将状态设置为已结束
    game->mStatus = END;
}

/**
 * 设置图片控件是否有边框。
 * @param game      游戏
 * @param hasBorder 有边框则为 TRUE，否则为 FALSE
 */
static void Game_SetPictureBorder(Game* game, BOOL hasBorder) {
    game->mPicHasBorder = hasBorder;

    // 设置所有图片控件的边框
    for (int i = 0; i < game->mDifficulty; i++) {
        game->mPictures[i]->setBorder(game->mPictures[i], hasBorder);
    }
}

/**
 * 游戏开始。
 * @param game  游戏
 */
static void Game_Start(Game* game) {
    POINT position = { 0 };
    int baseCount = (int)sqrt((double)game->mDifficulty);
    int index1 = 0, index2 = 0;

    // 重新创建所有图片
    if (game->mStatus != END) {
        Game_CreatePictures(game);
    }

    // 重置成员变量
    game->mStatus = PLAYING;
    game->mEmptyPoint.x = EMPTY_AREA_X + game->mPictures[0]->mSize * baseCount;
    game->mEmptyPoint.y = EMPTY_AREA_Y + game->mPictures[0]->mSize * (baseCount - 1);

    // 右下角图片控件右移
    game->mPictures[game->mDifficulty - 1]->getPosition(game->mPictures[game->mDifficulty - 1], &position);
    SetWindowPos(game->mPictures[game->mDifficulty - 1]->mHandle, HWND_TOP, game->mEmptyPoint.x, game->mEmptyPoint.y, 0, 0, SWP_NOSIZE);
    game->mEmptyPoint.x = position.x;
    game->mEmptyPoint.y = position.y;

    // 乱序其它图片控件的位置
    for (int i = 0; i < game->mDifficulty - 1; i++) {
        do {
            index1 = rand() % (game->mDifficulty - 1);
            index2 = rand() % (game->mDifficulty - 1);
        } while (index1 == index2);
        Game_SwapPictures(game, index1, index2);
    }
}

/**
 * 交换两个图片控件的位置。
 * @param game      游戏
 * @param index1    图片控件1的索引
 * @param index2    图片控件2的索引
 */
static void Game_SwapPictures(Game* game, int index1, int index2) {
    POINT position1 = { 0 }, position2 = { 0 };
    game->mPictures[index1]->getPosition(game->mPictures[index1], &position1);
    game->mPictures[index2]->getPosition(game->mPictures[index2], &position2);
    SetWindowPos(game->mPictures[index1]->mHandle, HWND_TOP, position2.x, position2.y, 0, 0, SWP_NOSIZE);
    SetWindowPos(game->mPictures[index2]->mHandle, HWND_TOP, position1.x, position1.y, 0, 0, SWP_NOSIZE);
}
